#include <vector>
#include <sstream>

class DynamicAbstHierarchy
{
private:
    std::vector<abstraction_t*> m_abst;

public:
    DynamicAbstHierarchy(const std::vector<abstraction_t*>& abst)
        : m_abst(abst)
    { };

    size_t NumLevels() const 
    { return m_abst.size(); }

    int StateSize() const 
    { return sizeof(state_t); }

    bool Equal(const void* s1, const void* s2) const
    { return compare_states((const state_t*)s1, (const state_t*)s2) == 0; }
    
    int64_t Hash(const void* state) const
    { return hash_state((const state_t*)state); }

    void Copy(void* dest, const void* from) const
    { copy_state((state_t*)dest, (const state_t*)from); }

    void PrintState(FILE *stream, const void* state) const
    { print_state(stream, (const state_t*)state); }

    bool IsGoal(const int i, const void* state) const
    { return is_dyn_goal((const state_t*)state, m_abst[i]); }

    void CopyGoalState(const int i, void* state, int n) const
    { 
        // FIXME: MAY NOT BE CORRECT!
        //  If base > ab1 > ab2 == base > ab2, then it's okay.
        //  Otherwise, for each goal state in the base level, we
        //  need to abstract the state up level by level to level i.
        init_dyn_goal_state((state_t*)state, n, m_abst[i]); }
    
    bool NextGoalState(const int i, void* state, int* n) const
    { return next_dyn_goal_state((state_t*)state, n, m_abst[i]); }

    int CostOfCheapestFwdRule(const int i) const
    { return COST_OF_CHEAPEST_FWD_RULE; }

    int CostOfCheapestBwdRule(const int i) const
    { return COST_OF_CHEAPEST_BWD_RULE; }

    void AbstractToNext(const int i, const void* in, void* out) const
    { abstract_state(m_abst[i+1], (const state_t*)in, (state_t*)out); }

    int Epsilon(const int i, const void* state) const
    {
        if (IsGoal(i, state))
            return 0;
        return COST_OF_CHEAPEST_FWD_RULE;
    }

    class FwdChildGenerator
    {
    public:
        FwdChildGenerator(const DynamicAbstHierarchy& ah, int level, 
                          const void* state)
            : m_abst(ah.m_abst[level]), m_state((const state_t*)state)
        {
            init_dyn_fwd_iter(m_iter);
        }
        
        bool Generate(void* child)
        {
            if ((m_rule_used = next_dyn_fwd_iter(&m_iter,m_state,m_abst))>=0) {
                apply_dyn_fwd_rule(m_rule_used, m_state, 
                                   (state_t*)child, m_abst);
                return true;
            }
            return false;
        }
        
        int Cost() const 
        { return fwd_rule_costs[m_rule_used]; }
        
        const char* RuleName() const 
        { return fwd_rule_names[m_rule_used]; }
        
    private:
        const abstraction_t* m_abst;
        const state_t* m_state;
        dyn_iter_t m_iter;
        int m_rule_used;
    };

    class FwdReverseChildGenerator
    {
    public:
        FwdReverseChildGenerator(const DynamicAbstHierarchy& ah, int level, 
                                 const void* state)
            : m_abst(ah.m_abst[level]),
              m_state((const state_t*)state),
              m_num(0)
        {
            dyn_iter_t iter;
            init_dyn_fwd_iter(iter);
            int rule_used;
            while ((rule_used = next_dyn_fwd_iter(&iter, m_state, m_abst))>=0) {
                m_rule[m_num++] = rule_used;
            }
            assert(m_num <= 128);
        }
        
        bool Generate(void *child)
        {
            if (m_num > 0) {
                m_last_rule = m_rule[--m_num];
                apply_dyn_fwd_rule(m_last_rule, m_state, 
                                   (state_t*)child, m_abst);
                return true;
            }
            return false;
        }
        
        int Cost() const { return fwd_rule_costs[m_last_rule]; }
        
        const char* RuleName() const { return fwd_rule_names[m_last_rule]; }
        
    private:
        const abstraction_t* m_abst;
        const state_t* m_state;
        int m_rule[128];
        int m_num;
        int m_last_rule;
    };

    class BwdChildGenerator
    {
    public:
        BwdChildGenerator(const DynamicAbstHierarchy& ah, int level, 
                          const void* state)
            : m_abst(ah.m_abst[level]), m_state((const state_t*)state)
        {
            init_dyn_bwd_iter(m_iter);
        }
        
        bool Generate(void* child)
        {
            if ((m_rule_used = next_dyn_bwd_iter(&m_iter,m_state,m_abst))>=0) {
                apply_dyn_bwd_rule(m_rule_used, m_state, 
                                   (state_t*)child, m_abst);
                return true;
            }
            return false;
        }
        
        int Cost() const 
        { return bwd_rule_costs[m_rule_used]; }
        
        const char* RuleName() const 
        { return bwd_rule_names[m_rule_used]; }
        
    private:
        const abstraction_t* m_abst;
        const state_t* m_state;
        dyn_iter_t m_iter;
        int m_rule_used;
    };
};

/* Reads an abstraction hierarchy and a set of problems to solve. */
bool ParseDynamicProblemInstances(std::vector<abstraction_t*>& abst,
                                  std::vector<state_t>& states)
{
    char token[1024];
    if (!scanf("%s", token))
        return false;
    if (strcasecmp(token, "instance"))
        return false;
    if (!scanf("%s", token) || token[0] != '{')
        return false;
    abst.push_back( create_identity_abstraction() );
    while (scanf("%s", token)) {
        if (!strcasecmp(token, "abstraction") ) {
            abstraction_t* ab = read_abstraction_from_stream( stdin );
            if (ab == NULL) {
                fprintf(stderr, "error reading abstraction.\n");
                return false;
            }
            abst.push_back(ab);
            //print_abstraction(ab);
        }
        else if (!strcasecmp(token, "state")) {
            if (!scanf(" %s", token) || token[0] != '{') {
                fprintf(stderr, "Missing opening '{'!\n");
                return false;
            }
            std::ostringstream os;
            while(scanf(" %s ", token)) {
                if (token[0] == '}')
                    break;
                os << token << ' ';
            }
            std::string line = os.str();
            state_t state;
            if (read_state(line.c_str(), &state) == -1) {
                fprintf(stderr, "Error reading state!\n");
                return false;
            }
            states.push_back(state);
        }
        else if (token[0] == '}')
            break;
    }
    return !states.empty();
}

/** NOTE:
    This is a temporary code for parsing data in the format used
    by Wheeler's group. 
 */
#include <algorithm>
bool ParseWheelerProblemInstance(std::vector<abstraction_t*>& abst,
                              std::vector<state_t>& states)
{
    char token[1024];
    abst.push_back( create_identity_abstraction() );

    int levels;
    if (!scanf("Abstractions:%d", &levels))
        return false;
    int dontcare = -1;
    for (int i = 1; i <= levels; ++i)
    {
        int n;
        if (!scanf(" %d", &n))
            return false;
        std::vector<int> care;
        for (int j = 0; j < n; ++j) {
            int k;
            if (!scanf(" %d", &k))
                return false;
            care.push_back(k);
        }
        if (dontcare == -1) {
            for (int j = 1; j < 16; ++j) {
                if (std::find(care.begin(), care.end(), j) == care.end()) {
                    dontcare = j;
                    break;
                }
            }
        }
        abstraction_t* ab = create_identity_abstraction();
        ab->value_map[0][0] = 0;
        for (int j = 1; j < 16; ++j) {
            if (std::find(care.begin(), care.end(), j) != care.end())
                ab->value_map[0][j] = j;
            else
                ab->value_map[0][j] = dontcare;
        }
        abstraction_compute_mapped_in(ab);
        //print_abstraction(ab);
        abst.push_back(ab);
    }

    if (!scanf(" %s", token) || strcasecmp(token, "Puzzle:")) {
        fprintf(stderr, "Missing opening 'Puzzle:'!\n");
        return false;
    }
    state_t state;
    for (int i = 0; i < 16; ++i) {
        int j;
        if (!scanf(" %d", &j))
            return false;
        if (j >= 16) 
            j = 0;
        state.vars[i] = j;        
    }
    states.push_back(state);
    //print_state(stdout, &state);
    return true;
}
